package com.gking.centerSystem.AOP;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.gking.centerSystem.AOP.annotationUtil.desensitizeData.DesensitizeData;
import com.gking.centerSystem.AOP.annotationUtil.desensitizeData.SensitizedTypeEnum;
import com.gking.centerSystem.common.Result;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.util.*;


@Slf4j
@Aspect
//@Component  // 移除组件声明，成为抽象类或者普通类，无法被识别为Bean，只提供通用的切面逻辑
abstract class DesensitizeDataAspect {


//    @Before：在方法执行之前执行。注解用于在目标方法执行之前执行相关逻辑。它不能改变目标方法的返回值，也不能控制目标方法是否执行。
//            使用场景：
//            日志记录：记录方法开始执行的时间、调用者信息等。
//            权限验证：在方法调用前检查用户是否有权限执行该操作。
//            输入参数检查：在目标方法执行前检查传入参数的有效性。

//    @After：在方法执行之后执行。注解用于在目标方法执行之后，无论是正常返回还是抛出异常，都会执行相关逻辑。
//            使用场景：
//            资源释放：在方法执行后释放某些资源（比如关闭数据库连接）。
//            日志记录：记录方法的执行时间或结束状态。
//            清理操作：在操作完成后进行一些清理工作。

//    @Around：在方法执行之前和之后执行，允许控制方法的执行。注解是最强大的，它可以在方法执行前、执行后都插入逻辑，并可以完全控制目标方法的执行（比如决定是否执行目标方法，或者改变其返回值）。
//            使用场景：
//            性能监控：计算方法执行时间。
//            权限控制：决定是否执行目标方法。
//            事务管理：可以在方法前后开始和提交事务

//    @AfterReturning：在方法成功返回后执行。注解用于在目标方法成功返回结果后执行，不会在方法抛出异常时触发。可以获取目标方法的返回值进行进一步处理。
//            使用场景：
//            记录返回值日志：在方法成功执行后记录返回结果。
//            数据处理：对方法的返回结果进行二次处理。
//            缓存更新：根据返回结果更新缓存。

//    @AfterThrowing：在方法抛出异常时执行。注解用于在目标方法抛出异常时执行，可以捕获异常信息并进行处理。
//            使用场景：
//            异常记录：记录方法执行过程中抛出的异常。
//            通知开发人员：如果出现特定的异常，发送通知给开发人员。
//            回滚操作：当方法抛出异常时，进行回滚或补偿操作。


//    @Around("@annotation(desensitizeData)")
//    public Object maskSensitiveData(ProceedingJoinPoint joinPoint, DesensitizeData desensitizeData) throws Throwable {
//        Object result = joinPoint.proceed(); // 执行原方法
//        if (result == null) {
//            return null;
//        }
//
//        log.info("desensitizeData desensitizeData : {} ", desensitizeData);
//        log.info("desensitizeData desensitizeData : {} ", result);
//
//        // 检查返回值是否是Map类型或者是对象类型，并根据注解中的字段名和脱敏类型进行处理
//
//        Map<String, String> map = new HashMap<>();
//        map.put("account", "acc****nt");
//        map.put("password", "******");
//        map.put("mail", "mail****@qq.com");
//
//        return Result.success("2333", map);
//    }


    /**
     * 校验当前用户脱敏认证凭证，子类继承后需重写该方法
     * 校验成功调用 setDataVerifyStatus() 方法设置认证状态
     *
     * @return 到期时间戳
     */
    public abstract long verify(String password);

    /**
     * 设置脱敏校验状态，子类继承后需重写该方法
     * 设置当前用户的脱敏校验状态
     *
     * @return 到期时间戳
     */
    public abstract long setDataVerifyStatus();

//    public long setDataVerifyStatus() {
//        // 1.获取登录用户唯一标识
//        String loginUser = "登录用户唯一标识";
//
//        // 2.以登录用户唯一标识为key，缓存用户设置的脱敏状态，设置好相应的脱敏认证有效时间（比如使用Redis, Session）
//        log.info("DesensitizeDataAspect 调用 setDataVerifyStatus");
//    }


    /**
     * 获取用户脱敏校验状态，子类继承后需重写该方法
     *
     * @return 如有校验成功状态，则返回 true，没有则返回false
     */
    public abstract boolean getDataVerifyStatus();

//    public boolean getDataVerifyStatus() {
//        // 1.获取登录用户唯一标识
//        String loginUser = "登录用户唯一标识";
//
//        // 2.以登录用户唯一标识为key，从缓存中提取出用户的脱敏状态
//        // 3.判断用户脱敏校验状态，如校验成功状态，则返回 true，没有则返回false
//        log.info("DesensitizeDataAspect 调用 getDataVerifyStatus");
//
//        return false;
//    }


    /**
     * 获取脱敏到期时间
     *
     * @return 到期时间戳，没有则返回 0L
     */
    public abstract long getDataVerifyExpireTime();

    /**
     * 刷新脱敏校验状态，也就是延长脱敏时间
     */
    public abstract void refreshDataVerifyStatus();


    // 针对某些脱敏数据的相关接口，进行校验处理，校验不通过无法编辑（修改、删除、禁用等）相关业务数据
    @Around("@annotation(desensitizeData)")
    public Object verifyStatus(ProceedingJoinPoint joinPoint, DesensitizeData desensitizeData) throws Throwable {
        // 判断是否要对该接口进行脱敏认证判断
        boolean needVerify = desensitizeData.needVerify();
        // 执行原方法
        if (!needVerify) return joinPoint.proceed();

        boolean dataVerifyStatus = getDataVerifyStatus();
        if (dataVerifyStatus) return joinPoint.proceed();
        else return Result.error("请先进行脱敏校验认证");
    }


    @AfterReturning(pointcut = "@annotation(desensitizeData)", returning = "result")
    public void desensitizeResponse(DesensitizeData desensitizeData, Result result) {
        // 如果返回为空，或者脱敏认证为以认证，则数据无需脱敏，直接返回明文
        boolean dataVerifyStatus = getDataVerifyStatus();
        if (result == null || dataVerifyStatus) {
            // 如果是认证成功的状态，则延长认证成功时间
            if (dataVerifyStatus) refreshDataVerifyStatus();
            return;
        }

        // 提取出要进行脱敏的字段和脱敏方式
        DesensitizeData.Field[] value = desensitizeData.value();
        Map<String, SensitizedTypeEnum> fieldItem = new HashMap<>();
        for (DesensitizeData.Field field : value) {
            fieldItem.put(field.field(), field.type());
        }

        // 检查返回值是否是Map类型或者是对象类型，并根据注解中的字段名和脱敏类型进行处理
        Object data = result.getData();
        if (data instanceof Page) {
            List<Object> recordTemp = new ArrayList<>();
            List<?> records = ((Page<?>) data).getRecords();

            for (Object record : records) {
                if (record instanceof Map) {
                    // 如果是Map类型，直接处理
                    Map<?, ?> map = desensitizeMap((Map<?, ?>) record, fieldItem);
                    recordTemp.add(map);
                } else {
                    // 如果是Java Bean类型，转换为Map再处理
                    Map<String, Object> map = convertObjectToMap(record);
                    Map<?, ?> desensitizedMap = desensitizeMap(map, fieldItem);
                    recordTemp.add(desensitizedMap);
                }
            }

            Page page = ((Page) data).setRecords(recordTemp);
            result.setData(page);
        } else {
            if (data instanceof Map) {
                Map<?, ?> map = desensitizeMap((Map<?, ?>) data, fieldItem);
                result.setData(map);
            } else {
                Map<String, Object> map = convertObjectToMap(data);
                Map<?, ?> desensitizedMap = desensitizeMap(map, fieldItem);
                result.setData(desensitizedMap);
            }
        }
    }


    /**
     * 将一个map中的字段进行脱敏
     *
     * @param map       要脱敏的map
     * @param fieldItem 要脱敏的字段和字段脱敏的方式
     * @return
     */
    private Map<?, ?> desensitizeMap(Map map, Map<String, SensitizedTypeEnum> fieldItem) {
        for (Map.Entry<String, SensitizedTypeEnum> entry : fieldItem.entrySet()) {
            String fieldName = entry.getKey(); //要脱敏的字段
            SensitizedTypeEnum type = entry.getValue(); //脱敏的方式

            Object fieldValue = map.get(fieldName);
            map.put(fieldName, applyDesensitization(fieldValue, type));
        }
        return map;
    }


    /**
     * 对一个值按照脱敏类型进行脱敏
     *
     * @param object 脱敏的值
     * @param type   脱敏方式
     * @return
     */
    private String applyDesensitization(Object object, SensitizedTypeEnum type) {
        try {
            if (object == null || object.equals("")) return null;

            String value = object.toString();

            switch (type) {
                case ALL:
                    return "******"; // 全部遮蔽
                case COMMON:
                    return value.replaceAll("(^.{3})(.*)(.{3}$)", "$1***$3");
                case PREFIX:
                    return value.replaceAll("(^.{4})(.*)", "$1***");
                case SUFFIX:
                    return value.replaceAll("(.{4}$)", "***$1");
                case MOBILE_PHONE:
                    return value.replaceAll("(\\d{3})\\d{4}(\\d{4})", "$1****$2");
                case EMAIL:
                    return value.replaceAll("(^[^@]{3})[^@]*(@.*$)", "$1***$2");
                case IP:
                    return value.replaceAll("(^.{3})(.*)(.{4}$)", "$1***$3");
                default:
                    return value;
            }
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }


    //  添加对象转换为 Map 的辅助方法
    //  你可以使用反射将 Java Bean 转换为 Map
    private static Map<String, Object> convertObjectToMap(Object obj) {
        Map<String, Object> map = new HashMap<>();
        Class<?> clazz = obj.getClass();

        while (clazz != null) { // 循环遍历所有父类
            Field[] fields = clazz.getDeclaredFields();
            for (Field field : fields) {
                field.setAccessible(true); // 确保可以访问私有字段
                try {
                    map.put(field.getName(), field.get(obj)); // 添加字段名和值到Map中
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
            clazz = clazz.getSuperclass(); // 获取父类
        }

        return map;
    }

//
//    @Before("execution(* com.gking.springboot.service.*.*(..))")
//    public void logBeforeMethod() {
//        System.out.println("Before method execution: LoggingAspect.logBeforeMethod()");
//    }
//    @After("execution(* com.gking.springboot.service.*.*(..))")
//    public void logAfterMethod() {
//        System.out.println("After method execution: LoggingAspect.logAfterMethod()");
//    }
//    @Around("execution(* com.gking.springboot.service.*.*(..))")
//    public Object logAroundMethod(ProceedingJoinPoint joinPoint) throws Throwable {
//        System.out.println("Before method execution: LoggingAspect.logAroundMethod()");
//        Object result = joinPoint.proceed(); // 调用目标方法
//        System.out.println("After method execution: LoggingAspect.logAroundMethod()");
//        return result;
//    }
//    @AfterReturning(pointcut = "execution(* com.gking.springboot.service.*.*(..))", returning = "result")
//    public void logAfterReturning(Object result) {
//        System.out.println("After method successfully returns: Result = " + result);
//    }
//    @AfterThrowing(pointcut = "execution(* com.gking.springboot.service.*.*(..))", throwing = "ex")
//    public void logAfterThrowing(Exception ex) {
//        System.out.println("After method throws exception: " + ex.getMessage());
//    }

}
